/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { HvigorNode } from '@ohos/hvigor';
import { OhosPluginId } from '@ohos/hvigor-ohos-plugin';
import { isHapModule, Logger, PluginFileUtil, PluginStore } from '../../framework';
import { HMRouterPluginConfig, HMRouterPluginConfigParam } from '../config/HMRouterPluginConfig';
import { HMRouterExtensionContext } from '../HMRouterExtensionContext';
import { FilePathConstants, TemplateConstants } from '../constants';

export class InitializerProcessor {
  private readonly context: HMRouterExtensionContext;

  constructor(context: HMRouterExtensionContext) {
    this.context = context;
  }

  execute(): void {
    // 1. Initialize configuration
    this.context.config = this.initConfig(this.context.node);
    this.context.routerMap = [];
    Logger.debug(this.context.node.getNodeName(), `read config finished, config: ${this.context.config}`);

    if (this.context.moduleIgnored) {
      Logger.debug(this.context.node.getNodeName(), `module ignored: initialize`);
      return;
    }

    // 2. Scan files
    this.scanFiles();
    Logger.debug(
      this.context.node.getNodeName(),
      `Scanned ${this.context.scanFiles.length} files ${this.context.scanFiles}`
    );
  }

  private scanFiles(): void {
    this.context.config.scanDir.forEach((dir) => {
      const scanPath = this.context.config.getScanPath(dir);
      this.deepScan(scanPath, '', this.context);
    });
  }

  private deepScan(scanPath: string, filePath: string, context: HMRouterExtensionContext): void {
    let resolvePath = PluginFileUtil.pathResolve(scanPath, filePath);
    if (!PluginFileUtil.exist(resolvePath)) {
      return;
    }
    if (PluginFileUtil.isDictionary(resolvePath)) {
      const files: string[] = PluginFileUtil.readdirSync(resolvePath);
      files.forEach((file) => {
        this.deepScan(resolvePath, file, context);
      });
    } else {
      context.scanFiles.push(resolvePath);
    }
  }

  /**
   * Read configuration
   * @param node Hvigor node
   * @returns Plugin configuration
   * @private
   */
  private initConfig(node: HvigorNode): HMRouterPluginConfig {
    // 1. Set app context
    this.setupAppContext(node);

    // 2. Read project and module configurations
    const projectConfigParam = this.readProjectConfig();
    const moduleConfigParam = this.readModuleConfig(node);

    // 3. Merge configurations
    const mergedConfigParam = this.mergeConfigs(projectConfigParam, moduleConfigParam);

    // 4. Create and return configuration instance
    return this.createConfigInstance(node, mergedConfigParam);
  }

  /**
   * Read configuration file
   * @param configPath Configuration path
   * @returns Configuration parameters
   * @private
   */
  private readConfigFile(configPath: string): HMRouterPluginConfigParam {
    let configParam: HMRouterPluginConfigParam = {};

    if (PluginFileUtil.exist(configPath)) {
      configParam = PluginFileUtil.readJson5(configPath);
    }

    return configParam;
  }

  /**
   * Read project configuration
   * @returns Project configuration parameters
   * @private
   */
  private readProjectConfig(): HMRouterPluginConfigParam {
    const projectPath = PluginStore.getInstance().get<string>('projectFilePath')!;
    const projectConfigFilePath = PluginFileUtil.pathResolve(projectPath, FilePathConstants.CONFIG_FILE_NAME);
    const configParam = this.readConfigFile(projectConfigFilePath);
    return this.resolveConfigPaths(configParam, projectPath);
  }

  /**
   * Read module configuration
   * @param node Hvigor node
   * @returns Module configuration parameters
   * @private
   */
  private readModuleConfig(node: HvigorNode): HMRouterPluginConfigParam {
    const modulePath = node.getNodePath();
    const moduleConfigFilePath = PluginFileUtil.pathResolve(modulePath, FilePathConstants.CONFIG_FILE_NAME);
    const configParam = this.readConfigFile(moduleConfigFilePath);
    return this.resolveConfigPaths(configParam, modulePath);
  }

  /**
   * Resolve relative paths in configuration to absolute paths
   * @param configParam Configuration parameters
   * @param basePath Base path for resolving relative paths
   * @returns Configuration parameters with resolved paths
   * @private
   */
  private resolveConfigPaths(configParam: HMRouterPluginConfigParam, basePath: string): HMRouterPluginConfigParam {
    if (configParam.defaultPageTemplate) {
      configParam.defaultPageTemplate = PluginFileUtil.pathResolve(basePath, configParam.defaultPageTemplate);
    }
    if (configParam.customPageTemplate) {
      configParam.customPageTemplate.forEach((item) => {
        item.templatePath = PluginFileUtil.pathResolve(basePath, item.templatePath);
      });
    }
    return configParam;
  }

  /**
   * Merge configurations
   * @param projectConfigParam Project configuration
   * @param moduleConfigParam Module configuration
   * @returns Merged configuration
   * @private
   */
  private mergeConfigs(
    projectConfigParam: HMRouterPluginConfigParam,
    moduleConfigParam: HMRouterPluginConfigParam
  ): HMRouterPluginConfigParam {
    // Default configuration
    const defaultConfig: HMRouterPluginConfigParam = {
      scanDir: [FilePathConstants.DEFAULT_SCAN_DIR],
      routerMapDir: FilePathConstants.DEFAULT_ROUTER_MAP_DIR,
      builderDir: FilePathConstants.DEFAULT_BUILD_DIR,
      defaultPageTemplate: TemplateConstants.VIEW_BUILDER_TEMPLATE,
      customPageTemplate: [],
      saveGeneratedFile: false,
      autoObfuscation: false,
    };

    // Merge configurations: default < project < module (shallow merge)
    return {
      ...defaultConfig,
      ...projectConfigParam,
      ...moduleConfigParam,
    };
  }

  /**
   * Set app context
   * @param node Hvigor node
   * @private
   */
  private setupAppContext(node: HvigorNode): void {
    const appContext = node.getParentNode()?.getContext(OhosPluginId.OHOS_APP_PLUGIN);
    if (appContext) {
      PluginStore.getInstance().set('projectFilePath', appContext.getProjectPath());
      PluginStore.getInstance().set('appContext', appContext);
      PluginStore.getInstance().set('buildMode', appContext.getBuildMode());
    }
  }

  /**
   * Create configuration instance
   * @param node Hvigor node
   * @param configParam Configuration parameters
   * @returns Plugin configuration instance
   * @private
   */
  private createConfigInstance(node: HvigorNode, configParam: HMRouterPluginConfigParam): HMRouterPluginConfig {
    const modulePath = node.getNodePath();

    // Get module name
    let nodeName = node.getNodeName();
    if (isHapModule(node)) {
      let packageJson: any = PluginFileUtil.readJson5(
        PluginFileUtil.pathResolve(node.getNodePath(), FilePathConstants.OH_PACKAGE_FILE_NAME)
      );
      nodeName = packageJson.name;
    }

    // Determine configDir: prioritize module-level config, fallback to project-level config
    let configDir = modulePath; // Default fallback
    const moduleConfigPath = PluginFileUtil.pathResolve(modulePath, FilePathConstants.CONFIG_FILE_NAME);
    const projectConfigPath = PluginFileUtil.pathResolve(
      PluginStore.getInstance().get<string>('projectFilePath')!,
      FilePathConstants.CONFIG_FILE_NAME
    );

    if (PluginFileUtil.exist(moduleConfigPath)) {
      // Module-level config exists, use module path as configDir
      configDir = modulePath;
    } else if (PluginFileUtil.exist(projectConfigPath)) {
      // Only project-level config exists, use project path as configDir
      configDir = PluginStore.getInstance().get<string>('projectFilePath')!;
    }
    // If neither exists, keep using modulePath as default

    return new HMRouterPluginConfig(nodeName, modulePath, configDir, configParam);
  }
}
